#include<head.h>
#define SIZE_BUF_MAX 1024

//线程参数结构体
typedef struct ThreadArg
{
    int src_fd;  // 源文件描述符
    int dest_fd; // 目标文件描述符
    off_t start; // 拷贝起始位置
    off_t end;   // 拷贝结束位置
} ThreadArg;

//文件拷贝
void *file_copy(void *arg)
{
    // 将 void* 类型的参数转换为 ThreadArg* 类型
    ThreadArg *thread = (ThreadArg *)arg;

    // 设定缓冲区
    char buf[SIZE_BUF_MAX];

    // 设定文件起始位置
    lseek(thread->src_fd, thread->start, SEEK_SET);
    lseek(thread->dest_fd,thread->start, SEEK_SET);

    // 开始拷贝
    int byte_read = 0;
    int byte_write = 0;

    // 循环读取源文件并写入目标文件，直到达到结束位置
    while (thread->start<thread->end)
    {
        if (0>=(byte_read = read(thread->src_fd, buf, SIZE_BUF_MAX)))
        {
            break;
        }

        if (0>=(byte_write = write(thread->dest_fd, buf, byte_read)))
        {
            break;
        }

        // 更新起始位置，已写入多少字节就移动多少偏移量          
        thread->start = thread->start + byte_write;
    }  
}

int main(int argc, char const *argv[])
{
    // 安全判定
    if (4!=argc)
    {
        printf("格式有误!\n请输入:%s file_dest file_src thread_num\n", argv[0]);
        return 0;
    }
   
    // 打开文件
    int dest_fd = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC, 0664); // 目标文件
    OPEN_ERR(dest_fd);

    int src_fd = open(argv[2], O_RDONLY); // 源文件
    OPEN_ERR(src_fd);

    // 获取文件状态
    struct stat file_stat; // 文件状态结构体

    if (fstat(src_fd, &file_stat) < 0)
    {
        perror("fstat err");
        close(dest_fd);
        close(src_fd);
        return 0;
    }

    int thread_num = atoi(argv[3]);         // 获取用户需要开辟的线程数
    // 获取文件大小 并计算每个进程 所拷贝的范围
    off_t file_size = file_stat.st_size; // 源文件大小
    off_t part_size = file_size / thread_num;

    pthread_t Tid[thread_num];         // 线程标识符数组
    ThreadArg thread_arg[thread_num]; // 线程参数数组

    // 创建线程
    for (int i = 0; i < thread_num; i++)
    {
        thread_arg[i].src_fd = src_fd;                                              // 设置源文件描述符
        thread_arg[i].dest_fd = dest_fd;                                            // 设置目标文件描述符
        thread_arg[i].start = i * part_size;                                        // 设置拷贝的起始位置
        thread_arg[i].end = (i == thread_num) ? file_size : part_size + thread_arg[i].start; // 设置拷贝的结束位置

        if (0 != pthread_create(&Tid[i], NULL, file_copy, (void *)&thread_arg[i]))
        {
            perror("pthread_create error:\n");
            close(src_fd);  // 关闭源文件描述符
            close(dest_fd); // 关闭目标文件描述符
            return 0;
        }
    }
    
    // 阻塞回收
    for (int i = 0; i < thread_num; i++)
    {
        pthread_join(Tid[i], NULL);
    }

    //关闭文件
    close(src_fd);  // 关闭源文件描述符
    close(dest_fd); // 关闭目标文件描述符

    return 0;
}

